/**
 * Copyright 2018-2019 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.modulelink.loader;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import com.jianggujin.modulelink.action.JAction;
import com.jianggujin.modulelink.config.JModuleConfig;
import com.jianggujin.modulelink.magager.JModuleManagerSupport;
import com.jianggujin.modulelink.module.JDefaultModule;
import com.jianggujin.modulelink.module.JModule;
import com.jianggujin.modulelink.module.JModuleListener;
import com.jianggujin.modulelink.support.annotation.JActionComponent;
import com.jianggujin.modulelink.support.annotation.JExclusion;
import com.jianggujin.modulelink.support.plugin.JInterceptor;
import com.jianggujin.modulelink.util.JModuleClassLoader;
import com.jianggujin.modulelink.util.JModuleUtils;
import com.jianggujin.modulelink.util.JResolverUtils;
import com.jianggujin.modulelink.util.JResolverUtils.JTest;

/**
 * 默认模块加载器解析器实现
 * 
 * @author jianggujin
 *
 */
public class JDefaultModuleLoaderSolver extends JAbstractModuleLoaderSolver implements JModuleLoaderSolver {

    @Override
    public <T extends JModuleConfig> JModule<T> load(T moduleConfig, JModuleClassLoader moduleClassLoader,
            JModuleManagerSupport<T> moduleManager) throws Exception {

        List<JModuleListener> listeners = this.loadModuleListeners(moduleConfig, moduleClassLoader, moduleManager);
        JDefaultModule<T> module = this.createModule(moduleManager, moduleConfig, moduleClassLoader, listeners);

        List<JInterceptor> globalInterceptors = moduleManager.getInterceptors();
        List<JInterceptor> interceptors = this.loadInterceptors(moduleConfig, moduleClassLoader);
        if (interceptors == null) {
            interceptors = globalInterceptors;
        } else if (globalInterceptors != null) {
            interceptors.addAll(globalInterceptors);
        }

        List<Object> actions = this.loadActions(moduleConfig, moduleClassLoader);
        this.initActions(moduleManager, interceptors, module, actions);

        module.initialize();
        return module;
    }

    /**
     * 加载模块监听器
     * 
     * @param moduleConfig
     * @param moduleClassLoader
     * @param moduleManager
     * @return
     * @throws Exception
     */
    protected List<JModuleListener> loadModuleListeners(JModuleConfig moduleConfig,
            JModuleClassLoader moduleClassLoader, JModuleManagerSupport<?> moduleManager) throws Exception {
        Set<String> scanPackages = moduleConfig.getScanPackages();
        boolean isNotEmpty = scanPackages != null && !scanPackages.isEmpty();
        List<JModuleListener> listeners = new ArrayList<JModuleListener>();
        // 查找模块监听器
        List<JModuleListener> moduleListeners = moduleManager.getModuleListeners();
        if (moduleListeners != null) {
            listeners.addAll(moduleListeners);
        }
        if (isNotEmpty) {
            listeners.addAll(this.scan(scanPackages, moduleConfig.getExclusionModuleListeners(), JModuleListener.class,
                    moduleClassLoader));
        }
        return listeners.isEmpty() ? null : Collections.unmodifiableList(listeners);
    }

    /**
     * 加载模块拦截器，不包含全局拦截器
     * 
     * @param moduleConfig
     * @param moduleClassLoader
     * @return
     * @throws Exception
     */
    protected List<JInterceptor> loadInterceptors(JModuleConfig moduleConfig, JModuleClassLoader moduleClassLoader)
            throws Exception {
        Set<String> scanPackages = moduleConfig.getScanPackages();
        boolean isNotEmpty = scanPackages != null && !scanPackages.isEmpty();
        // 查找拦截器
        if (isNotEmpty) {
            List<JInterceptor> interceptors = this.scan(scanPackages, moduleConfig.getExclusionInterceptors(),
                    JInterceptor.class, moduleClassLoader);
            JModuleUtils.sortOrder(interceptors);
            return interceptors;
        }
        return null;
    }

    protected List<Object> loadActions(JModuleConfig moduleConfig, JModuleClassLoader moduleClassLoader)
            throws Exception {
        Set<String> scanPackages = moduleConfig.getScanPackages();
        boolean isNotEmpty = scanPackages != null && !scanPackages.isEmpty();

        if (isNotEmpty) {
            List<Object> results = new ArrayList<Object>();
            JResolverUtils utils = new JResolverUtils();
            utils.setClassLoader(moduleClassLoader);
            Set<String> exclusions = moduleConfig.getExclusionActions();
            if (exclusions != null) {
                utils.setExclusions(exclusions);
            }
            utils.find(new JTest() {
                @Override
                public boolean matches(Class<?> type) {
                    if (type == null) {
                        return false;
                    }
                    if (JAction.class.isAssignableFrom(type) || type.isAnnotationPresent(JActionComponent.class)) {
                        if (type.isInterface() || Modifier.isAbstract(type.getModifiers())
                                || type.isAnnotationPresent(JExclusion.class)) {
                            return false;
                        }
                        return true;
                    }
                    return false;
                }
            }, scanPackages.toArray(new String[0]));
            for (Class<?> cls : utils.getClasses()) {
                results.add(cls.newInstance());
            }
            return results;
        }
        return null;
    }

    /**
     * 查找指定类型
     * 
     * @param <T>
     * @param scanPackages
     * @param exclusions
     * @param clazz
     * @param moduleClassLoader
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    protected <T> List<T> scan(Set<String> scanPackages, Set<String> exclusions, Class<T> clazz,
            JModuleClassLoader moduleClassLoader) throws Exception {
        List<T> results = new ArrayList<T>();
        JResolverUtils utils = new JResolverUtils();
        utils.setClassLoader(moduleClassLoader);
        if (exclusions != null) {
            utils.setExclusions(exclusions);
        }
        utils.findImplementations(clazz, scanPackages.toArray(new String[0]));
        for (Class<?> cls : utils.getClasses()) {
            if (cls.isInterface() || Modifier.isAbstract(cls.getModifiers())
                    || cls.isAnnotationPresent(JExclusion.class)) {
                continue;
            }
            results.add((T) cls.newInstance());
        }
        return results;
    }

    @Override
    public boolean matches(JModuleConfig moduleConfig, JModuleClassLoader moduleClassLoader) {
        return true;
    }
}